import { ContainerService } from "shared/component/container/ContainerService";
import InstanceMap from "shared/component/event/InstanceRemoveEvent";
import Log4R from "shared/component/log/Log4R";
import { randomProbability } from "shared/component/random/random";
import { CharacterState } from "shared/enums/CharacterState";
// const loginfo = Log4R.getLog('info');


export class AnimServiceImpl extends ContainerService<Ctn.AnimService, Ctn.AnimEntity> implements Ctn.AnimService {
    setSkillState(characterEntity: Ctn.character.CharacterEntity, skillState: Ctn.skill.State) {
        if (!characterEntity.character) return
        if (skillState.animChage) {
            this.playAnim({ animName: skillState.animChage }, characterEntity.character)
        }
        if (skillState.anim) {
            const skillStateAnim = skillState.anim
            if (typeIs(skillState.anim.speed, 'number')) {
                const state = this.getState(characterEntity.character);
                state?.currentAct.forEach(c => {
                    c.AdjustSpeed(skillStateAnim.speed)
                })
            }
        }
    }
    /** 动画暂停 */
    pause(characterEntity: Ctn.character.CharacterEntity, pauseEntity: TimeComponent.PauseEntity) {
        if (!characterEntity.character) return
        if (pauseEntity.pause) {
            const state = this.getState(characterEntity.character);
            state?.currentAct.forEach(c => {
                pauseEntity.pauseedStrack.push({//添加需要暂停的动画
                    speedBeforePause: c.Speed,
                    animationTrack: c
                })
                c.AdjustSpeed(0)
                c.TimePosition = pauseEntity.currentFrame / 30
            })
        } else {
            const state = this.getState(characterEntity.character);
            state?.currentAct.forEach(c => {
                const index = pauseEntity.pauseedStrack.findIndex(pauseed => pauseed.animationTrack === c);
                if (index > -1) {
                    const pauseed = pauseEntity.pauseedStrack[index];
                    pauseed.animationTrack.AdjustSpeed(pauseed.speedBeforePause);
                    pauseEntity.pauseedStrack.remove(index)

                }
            })
        }

    }
    /** 动画暂停 */
    step(characterEntity: Ctn.character.CharacterEntity, timePosition: number) {
        if (!characterEntity.character) return
        const state = this.getState(characterEntity.character);
        state?.currentAct.forEach(c => {
            // if (c.Speed !== 0) {
            //     c.AdjustSpeed(0)
            // }
            c.TimePosition = timePosition
        })

    }
    protected serverCalls: (keyof this)[] = ['getAnim'];
    protected serviceName: keyof Ctn.Services = "animService";
    /** 获取state */
    private getState(character: Model) {
        const characterEntity = this.getCharacter(character);
        return characterEntity?.animTable

    }
    /** 添加角色 */
    public addCharacter(config: Ctn.character.CharacterEntity) {
        if (!config.character) throw (config.character + ' is null')
        const characterEntity = this.getCharacter(config.character);
        if (!characterEntity) return
        /** 动画组 */
        const humanModel = config.character;
        const animGroup = config.animGroup || "default";
        const animWeight = this.getAnim(animGroup);
        if (!animWeight) throw ('not get anim weight:' + animGroup)
        const map = new Map<string, Animation>();

        characterEntity.animTable = {
            animationMap: map,
            animWeight: animWeight,
            currentAct: [],
        }

        const needLoad = []
        for (const [i, j] of pairs(animWeight)) {
            for (const [k, l] of pairs(j)) {
                let anim: Animation | undefined
                for (const [char, v] of pairs(this.getInstanceMap())) {
                    if (char !== humanModel) {
                        anim = v.animTable?.animationMap.get(l.id)
                        if (anim?.IsA('Animation')) {
                            map.set(l.id, anim);
                            break
                        }
                    }
                }
                if (!anim) {
                    anim = new Instance('Animation');
                    anim.AnimationId = l.id
                    needLoad.push(anim)
                    map.set(l.id, anim);
                }
            }
        } const ContentProvider = game.GetService('ContentProvider')
        ContentProvider.PreloadAsync(needLoad, (contentid, ass) => {
            if (ass === Enum.AssetFetchStatus.Success) {
                // loginfo?.info('success', contentid)
            }
        });
        this.playAnim({ animName: 'idle' }, humanModel)
    }
    public getAnim(anim: string): Ctn.anim.AnimWeight | undefined {
        const result = this.invoke('getAnim', anim);
        // loginfo?.info('get weight from server:', result)
        return result
    }
    private rollAnimation(animName: string, humanModel: Ctn.character.HumanModel) {
        const state = this.getState(humanModel);
        if (!state) throw ('not find instance' + humanModel)
        const animWeight = state.animWeight[animName];
        if (!animWeight) throw ('not find animWeight:' + animName)
        const id = randomProbability(animWeight)

        return id
    }
    playAnim(config: {
        animName: string, transitionTime?: number, speed?: number, duration?: number
    }, humanModel: Ctn.character.HumanModel,) {
        const state = this.getState(humanModel);
        const animId = this.rollAnimation(config.animName, humanModel);
        const anim = state?.animationMap.get(animId);
        if (!anim?.IsA('Animation')) throw ('const find animId:' + animId)
        return this.switchToAnim({
            anim,
            ...config
        }, humanModel)
    }
    switchToAnim(config: {
        anim: Animation, transitionTime?: number, speed?: number, duration?: number
    }, humanModel: Ctn.character.HumanModel,) {
        let act: AnimationTrack | undefined;
        const state = this.getState(humanModel);
        if (!state) throw ('not find instance' + humanModel)
        // if (config.anim !== state.currentActInstance) {

        if (config.speed === undefined) {
            config.speed = 1
        }
        const humanoid = humanModel.Humanoid;

        if (!humanoid) {

            throw error('nohumanoid');
            return
        };
        state.currentAct.forEach(c => {
            c.Stop(config.transitionTime);
            c.Destroy();
        })
        state.currentAct.clear()
        act = humanModel.Humanoid.Animator.LoadAnimation(config.anim);
        state.currentAct.push(act)
        if (config.duration) {
            config.speed = act.Length / config.duration
        }
        state.currentActInstance = config.anim

        act.Play(config.transitionTime, undefined, config.speed);
        // loginfo?.info("play", act)
        // } else if (state.currentAct.size() > 0) {
        //     state.currentAct[0].AdjustSpeed(config.speed)
        //     return state.currentAct[0]
        // }
        return act
    }
    getHeightScale(humanModel: Ctn.character.HumanModel) {
        return 7
    }

    changeState(p: Ctn.CharacterStateEntity, humanModel: Ctn.character.HumanModel) {
        switch (p.state?.actStates) {

            case CharacterState.Skilling:
                if (p.anim)
                    this.playAnim({ animName: p.anim, transitionTime: p.transitionTime }, humanModel)
                break;
            case CharacterState.Running:
                this.playAnim({ animName: 'run', transitionTime: p.transitionTime }, humanModel)
                break;
            case CharacterState.WallHeaderHang:
                this.playAnim({ animName: 'wallHeaderHang', transitionTime: p.transitionTime }, humanModel)
                break;
            case CharacterState.WallHeaderClimb:
                this.playAnim({ animName: 'wallHeaderClimb', transitionTime: p.transitionTime }, humanModel)
                break;
            case CharacterState.Standing:
                this.playAnim({ animName: 'idle', transitionTime: p.transitionTime }, humanModel)
                break
            case CharacterState.Crouch:
                this.playAnim({ animName: 'crouch', transitionTime: p.transitionTime }, humanModel)
                break
            case CharacterState.Jumping:
                this.playAnim({ animName: 'jump', transitionTime: p.transitionTime }, humanModel)
                break
            case CharacterState.FreeFalling:
                this.playAnim({ animName: 'fall', transitionTime: p.transitionTime }, humanModel)
                break
            case CharacterState.DownFalling:
                this.playAnim({ animName: 'fall', transitionTime: p.transitionTime }, humanModel)
                break
            // case CharacterState.DownJumping:
            //     this.playAnim({ animName: 'fall', transitionTime: p.transitionTime }, humanModel)
            //     break
            case CharacterState.Climbing:
                // loginfo?.info('p.speed ', p.speed)
                if (p.speed !== undefined) {
                    this.playAnim({
                        speed: (p.speed) / this.getHeightScale(humanModel),
                        animName: 'climb', transitionTime: p.transitionTime
                    }, humanModel)
                }

                break
        }
    }
}